package com.seadragon.app.ksr.controller;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import javax.persistence.NoResultException;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.validation.Valid;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.propertyeditors.CustomCollectionEditor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.support.SessionStatus;

import com.seadragon.app.ksr.dao.CategoryDao;
import com.seadragon.app.ksr.model.Business;
import com.seadragon.app.ksr.model.Category;
import com.seadragon.app.ksr.model.Customer;
import com.seadragon.app.ksr.model.KsrConstants;
import com.seadragon.app.ksr.model.Office;
import com.seadragon.app.ksr.service.BusinessService;
import com.seadragon.app.ksr.service.CustomerService;
import com.seadragon.app.ksr.util.CacheManager;

@Controller
public class LoginController {
	private static Logger logger = LoggerFactory.getLogger(LoginController.class);

	@Autowired
	private BusinessService businessService;
	
	@Autowired
	private CacheManager cacheManager;
	
	@Autowired
	private CustomerService customerService;
	
	@Autowired
	private CategoryDao categoryDao;

	private MessageDigest md;

	public LoginController() {
		try {
			md = MessageDigest.getInstance("SHA-256");
		} catch (NoSuchAlgorithmException ex) {
			logger.error("Failed to create MessageDigest", ex);
		}
	}

	private String encrypt(String password) {
		byte[] raw = md.digest(password.getBytes());
		return new String(Base64.encodeBase64(raw));
	}

	@ModelAttribute
	public void populateModel(Model model) {
//		model.addAttribute("categoryList", CacheManager.getAllLeafCatogories());
		model.addAttribute("categoryList", categoryDao.findAllByLevel(1));
	}

	@InitBinder
	protected void initBinder(WebDataBinder binder) {
		binder.registerCustomEditor(Set.class, "categories", new CustomCollectionEditor(Set.class) {
			@Override
			protected Object convertElement(Object element) {
				Category category = null;
				if (element instanceof String && StringUtils.isNotEmpty((String) element)) {
					category = new Category();
					for (Category categ : cacheManager.getAllLeafCatogories()) {
						if (categ.getId() == new Long((String) element).longValue()) {
							BeanUtils.copyProperties(categ, category);
							break;
						}
					}
				}
				return category;
			}
		});
	}

	@RequestMapping(value = "/signup.html", method = RequestMethod.GET)
	public String showSignup() {
		return "customer.signup";
	}

	@RequestMapping(value = "/business/signup.html", method = RequestMethod.GET)
	public String businessSignup(Model model) {
		return "business.signup";
	}

	@RequestMapping(value = "/login.html", method = RequestMethod.GET)
	public String showLogin() {
		return "customer.login";
	}

	@RequestMapping(value = "/business/login.html", method = RequestMethod.GET)
	public String businessLogin() {
		return "business.login";
	}


	@RequestMapping(value = "/login.html", method = RequestMethod.POST)
	public String doCustomerlogin(HttpSession session, ServletRequest request, Model model,
			@RequestParam(value = "email", required = true) String email, @RequestParam(value = "password", required = true) String password) {
		logger.info("Email: " + email);
		logger.info("Password: " + password);
		boolean loginSuccessful = doLogin(session, model, email, password);
		return (loginSuccessful) ? "redirect:/index.html" : "customer.login";
	}
	
	
	// check the availability of the username
	@RequestMapping(value = "/login.json", method = RequestMethod.POST)
	public @ResponseBody
	String doLoginByAjax(HttpSession session, ServletRequest request, Model model,
			@RequestParam(value = "email", required = true) String email, @RequestParam(value = "password", required = true) String password) {
		logger.info("Email: " + email);
		logger.info("Password: " + password);
		boolean loginSuccessful = doLogin(session, model, email, password);
		return (loginSuccessful) ? "true" : "Invalid username or passowrd";
	}

	private boolean doLogin(HttpSession session, Model model, String email, String password) {
		boolean loginSuccessful = true;
		Customer customer = null;
		try {
			customer = customerService.findByEmail(email);
		} catch (NoResultException e) {
			logger.error("customer with email = " + email + " not found!", e);
		}
		if (customer == null || !customer.getPassword().equalsIgnoreCase(encrypt(password))) {
			loginSuccessful = false;
			List<String> errMsgs = new ArrayList<String>();
			errMsgs.add("Invalid username or passowrd");
			model.addAttribute("errMsgs", errMsgs);
		} else {
			session.setAttribute(KsrConstants.SESSION_SCOPED_CUSTOMER, customer);
		}
		return loginSuccessful;
	}
	@RequestMapping(value = "/business/login.html", method = RequestMethod.POST)
	public String doBusinesslogin(HttpSession session, ServletRequest request, Model model,
			@RequestParam(value = "email", required = true) String email, @RequestParam(value = "password", required = true) String password) {
		logger.info("Email: " + email);
		logger.info("Password: " + password);
		boolean loginSuccessful = true;
		Business business = null;
		try {
			business = businessService.findByEmail(email);
		} catch (NoResultException e) {
			logger.error("Business with email = " + email + " not found!", e);
		}
		if (business == null || !business.getPassword().equalsIgnoreCase(encrypt(password))) {
			loginSuccessful = false;
			List<String> errMsgs = new ArrayList<String>();
			errMsgs.add("Invalid username or passowrd");
			model.addAttribute("errMsgs", errMsgs);
		} else {
			session.setAttribute(KsrConstants.SESSION_SCOPED_BUSINESS, business);
		}
		
		return (loginSuccessful) ? "redirect:/business/index.html" : "business.login";
	}

	@RequestMapping(value = "/logout.html", method = RequestMethod.GET)
	public String doCustomerLogout(HttpServletRequest request, HttpSession session, Model model) {
		session.removeAttribute(KsrConstants.SESSION_SCOPED_CUSTOMER);
		return "customer.login";
	}
	
	@RequestMapping(value = "/business/logout.html", method = RequestMethod.GET)
	public String doBusinessLogout(HttpServletRequest request, HttpSession session, Model model) {
		session.removeAttribute(KsrConstants.SESSION_SCOPED_BUSINESS);
		return "business.login";
	}

	@RequestMapping(value = "/signup.html", method = RequestMethod.POST)
	public String customerRegristration(@ModelAttribute("customer") @Valid Customer customer, BindingResult result, SessionStatus status,
			HttpServletRequest request, Model model) {
		if (customer != null) {
			customer.setPassword(encrypt(customer.getPassword()));
		}
		customerService.save(customer);
		request.getSession().setAttribute(KsrConstants.SESSION_SCOPED_CUSTOMER, customer);
		model.addAttribute("customer", customer);
		return "customer.home";
	}

	//ajax sign up
	@RequestMapping(value = "/signup.json", method = RequestMethod.POST)
	public @ResponseBody
	String customerRegistrationByAjax(@ModelAttribute("customer") @Valid Customer customer, BindingResult result, SessionStatus status,
			HttpServletRequest request, Model model) {
		if (customer != null) {
			customer.setPassword(encrypt(customer.getPassword()));
		}
		customerService.save(customer);
		request.getSession().setAttribute(KsrConstants.SESSION_SCOPED_CUSTOMER, customer);
		return "true";
	}
	
	@RequestMapping(value = "/business/signup.html", method = RequestMethod.POST)
	public String businessRegristration(@ModelAttribute("business") @Valid Business business, BindingResult result, SessionStatus status,
			HttpServletRequest request, Model model) {
		if (business != null) {
			business.setPassword(encrypt(business.getPassword()));
			for (Office office : business.getOffices()) {
				office.setBusiness(business);
			}
		}
		request.getSession().setAttribute(KsrConstants.SESSION_SCOPED_BUSINESS, business);
		businessService.save(business);
		model.addAttribute("business", business);
		return "business.home";
	}

	// check the availability of the username
	@RequestMapping(value = "/check-email.json", method = RequestMethod.GET)
	public @ResponseBody
	boolean checkCustomerEmail(HttpServletRequest request, HttpSession session, @RequestParam String email) {
		Customer customer = null;
		try {
			customer = customerService.findByEmail(email);

		} catch (NoResultException e) {
			logger.error("No customer with emailAddress= " + email + " signed up.", e);
		} catch (Exception e) {
			logger.error("Failed to get customer for emailAddress= " + email, e);
		}
		Customer current = (Customer) session.getAttribute(KsrConstants.SESSION_SCOPED_CUSTOMER);
		// for user profile update, email can be the one of herself
		return customer == null || (current != null && email.equals(current.getEmail()));
	}
	
	// check the availability of the username
	@RequestMapping(value = "/business/check-email.json", method = RequestMethod.GET)
	public @ResponseBody
	boolean checkBusinessEmail(HttpServletRequest request, HttpSession session, @RequestParam String emailAddress) {
		Business business = null;
		try {
			business = businessService.findByEmail(emailAddress);
			
		} catch (NoResultException e) {
			logger.error("No business with emailAddress= " + emailAddress + " signed up.", e);
		} catch (Exception e) {
			logger.error("Failed to get business for emailAddress= " + emailAddress, e);
		}
		Business current = (Business) session.getAttribute(KsrConstants.SESSION_SCOPED_BUSINESS);
		// for user profile update, username can be the one of herself
		return business == null || (current != null && emailAddress.equals(current.getEmailAddress()));
	}

	@RequestMapping(value = "/profile.html", method = RequestMethod.GET)
	public String showCustomerProfile(HttpServletRequest request, HttpSession session, Model model) {
		if (session.getAttribute(KsrConstants.SESSION_SCOPED_CUSTOMER) != null) {
			model.addAttribute("customer", session.getAttribute(KsrConstants.SESSION_SCOPED_CUSTOMER));
		}
		return "customer.profile";
	}
	
	@RequestMapping(value = "/business/profile.html", method = RequestMethod.GET)
	public String showBusinessProfile(HttpServletRequest request, HttpSession session, Model model) {
		if (session.getAttribute(KsrConstants.SESSION_SCOPED_BUSINESS) != null) {
			model.addAttribute("business", session.getAttribute(KsrConstants.SESSION_SCOPED_BUSINESS));
		}
		return "business.profile";
	}

	@RequestMapping(value = "/profile.html", method = RequestMethod.POST)
	public String updateCustomerProfile(@ModelAttribute("customer") @Valid Customer customer, BindingResult result, HttpSession session, Model model) {
		Customer customerInSession = (Customer) (session.getAttribute(KsrConstants.SESSION_SCOPED_CUSTOMER));
		if (customer != null && customerInSession != null) {
			customer.setId(customerInSession.getId());
			if (StringUtils.isNotEmpty(customer.getPassword())) {
				customer.setPassword(encrypt(customer.getPassword()));
			} else {
				customer.setPassword(customerInSession.getPassword());
			}
		}
		// update customer
		customerService.update(customer);
		session.setAttribute(KsrConstants.SESSION_SCOPED_CUSTOMER, customer);
		model.addAttribute("customer", customer);
		return "customer.profile";
	}
	
	@RequestMapping(value = "/business/profile.html", method = RequestMethod.POST)
	public String updateBusinessProfile(@ModelAttribute("business") @Valid Business business, BindingResult result, HttpSession session, Model model) {
		Business businessInSession = (Business) (session.getAttribute(KsrConstants.SESSION_SCOPED_BUSINESS));
		if (business != null && businessInSession != null) {
			business.setId(businessInSession.getId());
			if (StringUtils.isNotEmpty(business.getPassword())) {
				business.setPassword(encrypt(business.getPassword()));
			} else {
				business.setPassword(businessInSession.getPassword());
			}
			for (Office office : business.getOffices()) {
				office.setBusiness(business);
			}
		}
		// update business
		businessService.update(business);
		session.setAttribute(KsrConstants.SESSION_SCOPED_BUSINESS, business);
		model.addAttribute("business", business);
		return "business.profile";
	}
}
